Die Boost C++ Bibliotheken


Kapitel 10: Zeitangaben


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


10.1 Allgemeines

Die Bibliothek Boost.DateTime kann verwendet werden, um Zeitangaben wie Kalenderdaten und Uhrzeiten zu verarbeiten. Darüberhinaus bietet Boost.DateTime Erweiterungen an, um zum Beispiel Zeitzonen zu berücksichtigen. Außerdem wird die formatierte Ein- und Ausgabe von Kalenderdaten und Uhrzeiten unterstützt. In diesem Kapitel lernen Sie all die verschiedenen Bestandteile von Boost.DateTime kennen.


10.2 Kalenderdaten

Boost.DateTime unterstützt lediglich Kalenderdaten basierend auf dem Gregorianischen Kalender. Das ist in der Praxis insofern kein Problem als dass der Gregorianische Kalender der heute weltweit am weitesten verbreitete Kalender ist. So können Sie davon ausgehen, dass Sie, wenn Sie sich mit einem Freund, der aus einem anderen Land stammt, zum Beispiel für den 5. Januar 2010 verabreden, ihm nicht extra mitteilen müssen, dass sich das Datum auf den Gregorianischen Kalender bezieht.

Der Gregorianische Kalender wurde von Papst Gregor XIII. im Jahr 1582 eingeführt. Das heißt, dass erst seit diesem Jahr mit dem Gregorianischen Kalender gerechnet wird. Das ist insofern wichtig als dass Boost.DateTime wie erwähnt ausschließlich den Gregorianischen Kalender unterstützt. Genaugenommen werden Datumsangaben für die Jahre 1400 bis 9999 unterstützt. Wie Sie sehen reicht die Unterstützung über das Jahr 1582 hinaus bis zurück ins Jahr 1400. Falls Sie tatsächlich mit Datumsangaben vor 1582 arbeiten, können Sie Boost.DateTime verwenden, wenn Sie die Datumsangaben in den Gregorianischen Kalender umrechnen und nur bis ins Jahr 1400 zurückgehen müssen. Andernfalls müssen Sie auf eine andere Bibliothek zugreifen und können Boost.DateTime nicht verwenden.

Der Namensraum, in dem Boost.DateTime Klassen und Funktionen zur Verarbeitung von Kalenderdaten zur Verfügung stellt, ist boost::gregorian. Sie können alle Klassen und Funktionen aus diesem Namensraum einsetzen, wenn Sie die Headerdatei boost/date_time/gregorian/gregorian.hpp einbinden. So können Sie dann zum Beispiel auf die Klasse boost::gregorian::date zugreifen, um ein Datum zu erstellen.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d(2010, 1, 30); 
  std::cout << d.year() << std::endl; 
  std::cout << d.month() << std::endl; 
  std::cout << d.day() << std::endl; 
  std::cout << d.day_of_week() << std::endl; 
  std::cout << d.end_of_month() << std::endl; 
} 

Die Klasse boost::gregorian::date bietet mehrere Konstruktoren an, um ein Datum zu erstellen. Dem einfachsten Konstruktor wird ein Jahr, Monat und Tag übergeben. Wird ein ungültiger Wert angegeben, wird eine Ausnahme vom Typ boost::gregorian::bad_year, boost::gregorian::bad_month oder boost::gregorian::bad_day_of_month geworfen, die alle von std::out_of_range abgeleitet sind.

Wie Sie anhand des obigen Beispiels sehen, stehen zahlreiche Methoden zur Verfügung, um auf ein Datum zuzugreifen. Während über Methoden wie year(), month() und day() auf die Werte zugegriffen werden kann, mit denen das Objekt initialisiert wurde, können mit Methoden wie day_of_week() und end_of_month() Werte erhalten werden, die selbst zu errechnen aufwändig wäre.

Während Sie dem Konstruktor von boost::gregorian::date wie im obigen Beispiel Zahlen übergeben können, um ein Datum zu setzen, wird aber zum Beispiel beim Aufruf von month() Jan und beim Aufruf von day_of_week() Sat ausgegeben. Es handelt sich dabei also nicht um einfache Zahlen, sondern um Werte vom Typ boost::gregorian::date::month_type und boost::gregorian::date::day_of_week_type. Wie Sie später in diesem Kapitel noch sehen werden, bietet Boost.DateTime eine umfangreiche Unterstützung zur formatierten Ein- und Ausgabe an, so dass Sie zum Beispiel die Ausgabe so anpassen können, dass anstatt von Jan der Wert 1 ausgegeben wird.

Beachten Sie, dass der Standardkonstruktor von boost::gregorian::date ein ungültiges Datum erstellt. Sie können ein derart ungültiges Datum explizit erstellen, wenn Sie boost::date_time::not_a_date_time als einzigen Parameter an den Konstruktor von boost::gregorian::date übergeben.

Neben dem direkten Aufruf eines Konstruktors kann ein Objekt vom Typ boost::gregorian::date auch über freistehende Funktionen und Methoden anderer Objekte erstellt werden.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d = boost::gregorian::day_clock::universal_day(); 
  std::cout << d.year() << std::endl; 
  std::cout << d.month() << std::endl; 
  std::cout << d.day() << std::endl; 

  d = boost::gregorian::date_from_iso_string("20100131"); 
  std::cout << d.year() << std::endl; 
  std::cout << d.month() << std::endl; 
  std::cout << d.day() << std::endl; 
} 

Im obigen Programm wird die Klasse boost::gregorian::day_clock verwendet. Es handelt sich dabei um eine Uhr, die das aktuelle Datum zurückgibt. Die Methode universal_day() gibt ein UTC-Datum zurück, das unabhängig von Zeitzonen und Sommerzeit ist. UTC ist die internationale Abkürzung für die Weltzeit, der Mitteleuropa eine Stunde voraus ist. Neben universal_day() bietet boost::gregorian::day_clock auch eine Methode local_day() an, die die Einstellungen auf einem Computer bezüglich Zeitzone und Sommerzeit berücksichtigt. Sie müssen also local_day() verwenden, wenn Sie ein aktuelles Datum in der Zeitzone erhalten wollen, in der Sie sich befinden.

Im Namensraum boost::gregorian stehen außerdem zahlreiche freistehende Funktionen zur Verfügung, um ein Datum in einem String in ein Objekt vom Typ boost::gregorian::date umzuwandeln. So wird im obigen Programm mit der Funktion boost::gregorian::date_from_iso_string() ein Datum im ISO 8601-Format umgewandelt. Boost.DateTime bietet weitere freistehende Funktionen an wie boost::gregorian::from_simple_string() und boost::gregorian::from_us_string().

Während Sie mit boost::gregorian::date eine Klasse kennengelernt haben, die als Datum einen Zeitpunkt markiert, bietet Boost.DateTime auch eine Klasse boost::gregorian::date_duration für einen Zeitraum an.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d1(2008, 1, 31); 
  boost::gregorian::date d2(2008, 8, 31); 
  boost::gregorian::date_duration dd = d2 - d1; 
  std::cout << dd.days() << std::endl; 
} 

Da die Klasse boost::gregorian::date den Operator operator-() überladt, können wie im obigen Beispiel zwei Zeitpunkte mit dem Minuszeichen verknüpft werden. Der Rückgabewert hat den Typ boost::gregorian::date_duration und entspricht dem Zeitraum zwischen den beiden Daten.

Die wichtigste Methode, die boost::gregorian::date_duration anbietet, ist days(). Sie gibt die Anzahl der Tage zurück, aus denen der Zeitraum besteht.

Sie können ein Objekt vom Typ boost::gregorian::date_duration auch selbst erstellen. Sie übergeben in diesem Fall dem Konstruktor als einzigen Parameter die Anzahl der Tage. Wenn Sie einen Zeitraum erstellen wollen, der Wochen, Monate oder Jahre umfasst, können Sie auf die Klassen boost::gregorian::weeks, boost::gregorian::months und boost::gregorian::years zugreifen.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date_duration dd(4); 
  std::cout << dd.days() << std::endl; 
  boost::gregorian::weeks ws(4); 
  std::cout << ws.days() << std::endl; 
  boost::gregorian::months ms(4); 
  std::cout << ms.number_of_months() << std::endl; 
  boost::gregorian::years ys(4); 
  std::cout << ys.number_of_years() << std::endl; 
} 

Die Klassen boost::gregorian::months und boost::gregorian::years bieten keine Methoden an, um die Anzahl an Tagen zu ermitteln. Das sollte einleuchtend sein, da Monate und Jahre unterschiedlich lang sein können. Warum es dennoch Sinn machen kann, diese Klassen zu verwenden, sehen Sie im folgenden Beispiel.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d(2009, 1, 31); 
  boost::gregorian::months ms(1); 
  boost::gregorian::date d2 = d + ms; 
  std::cout << d2 << std::endl; 
  boost::gregorian::date d3 = d2 - ms; 
  std::cout << d3 << std::endl; 
} 

Im obigen Programm wird zum 31. Januar 2009 ein Monat hinzuaddiert. Boost.DateTime errechnet für d2 daraufhin den 28. Februar 2009. Im nächsten Schritt wird von diesem Datum wieder ein Monat abgezogen, woraufhin für d3 wieder der 31. Januar 2009 errechnet wird. Wie Sie sehen lassen sich mit Zeitpunkten und -räumen Berechnungen anstellen. Sie müssen jedoch auf Besonderheiten achten wie die, dass Sie ausgehend vom letzten Tag eines Monats immer zum letzten Tag in einem anderen Monat gelangen, wenn Sie mit boost::gregorian::months vorwärts oder zurückspringen. Das kann zu unerwarteten Ergebnissen führen.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d(2009, 1, 30); 
  boost::gregorian::months ms(1); 
  boost::gregorian::date d2 = d + ms; 
  std::cout << d2 << std::endl; 
  boost::gregorian::date d3 = d2 - ms; 
  std::cout << d3 << std::endl; 
} 

Obiges Programm ist identisch mit dem vorherigen. Der einzige Unterschied ist, dass die Variable d mit dem 30. Januar 2009 initialisiert wurde. Obwohl es sich hierbei nicht um den letzten Tag im Januar handelt, wird nach dem Sprung vorwärts für d2 natürlich der 28. Februar 2009 errechnet - schließlich gibt es keinen 30. Februar. Wenn im letzten Schritt wieder ein Monat zurückgesprungen wird, wird für d3 jedoch der 31. Januar 2009 errechnet! Schließlich ist der 28. Februar 2009 der letzte Tag in diesem Monat, so dass Sie bei einem Sprung zurück zum letzten Tag im Januar gelangen.

Wenn Sie das für verwirrend halten, können Sie die Definition des Makros BOOST_DATE_TIME_OPTIONAL_GREGORIAN_TYPES aufheben. Sie können dann die Klassen boost::gregorian::weeks, boost::gregorian::months und boost::gregorian::years nicht verwenden. Da dann lediglich die Klasse boost::gregorian::date_duration zur Verfügung steht, mit der einfach eine bestimmte Anzahl an Tagen vorwärts oder rückwärts gezählt wird, kann es nicht mehr zu möglicherweise unerwarteten Ergebnissen kommen.

Während Sie mit boost::gregorian::date_duration eine Klasse kennengelernt haben, die einen beliebigen Zeitraum umfasst, können Sie mit boost::gregorian::date_period eine Periode angeben, die an einem bestimmten Datum beginnt und an einem bestimmten Datum endet.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d1(2009, 1, 30); 
  boost::gregorian::date d2(2009, 10, 31); 
  boost::gregorian::date_period dp(d1, d2); 
  boost::gregorian::date_duration dd = dp.length(); 
  std::cout << dd.days() << std::endl; 
} 

Dem Konstruktor von boost::gregorian::date_period werden so wie im obigen Beispiel zwei Parameter vom Typ boost::gregorian::date übergeben, die den Anfangs- und Endzeitpunkt bestimmen. Alternativ kann auch ein Anfangszeitpunkt und eine Zeitdauer vom Typ boost::gregorian::date_duration angegeben werden. Beachten Sie, dass in beiden Fällen der Endzeitpunkt nicht zur Periode zählt, sondern der Tag vor dem Endzeitpunkt der letzte Tag in der Periode ist. Das ist wichtig, um zu verstehen, welche Ergebnisse das folgende Programm ausgibt.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d1(2009, 1, 30); 
  boost::gregorian::date d2(2009, 10, 31); 
  boost::gregorian::date_period dp(d1, d2); 
  std::cout << dp.contains(d1) << std::endl; 
  std::cout << dp.contains(d2) << std::endl; 
} 

Im obigen Programm wird mit Hilfe der Methode contains() getestet, ob ein bestimmes Datum innerhalb der Periode liegt. Obwohl dem Konstruktor von boost::gregorian::date_period die beiden Variablen d1 und d2 übergeben wurden, gibt contains() lediglich beim ersten Aufruf true zurück. Weil der Endzeitpunkt nicht zur Periode zählt, wird beim Aufruf von contains() mit d2 als Parameter false zurückgegeben.

Die Klasse boost::gregorian::date_period bietet weitere Methoden an, um zum Beispiel eine Periode zu verschieben oder die Schnittmenge zweier sich überschneidender Perioden zu ermitteln.

Neben Klassen für Daten, Zeiträume und Perioden bietet Boost.DateTime Iteratoren und verschiedene nützliche freistehende Funktionen an. Sehen Sie sich dazu folgendes Beispiel an.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::gregorian::date d(2009, 1, 5); 
  boost::gregorian::day_iterator it(d); 
  std::cout << *++it << std::endl; 
  std::cout << boost::date_time::next_weekday(*it, boost::gregorian::greg_weekday(boost::date_time::Friday)) << std::endl; 
} 

Der Iterator boost::gregorian::day_iterator ermöglicht es, von einem bestimmten Datum ausgehend schrittweise um einen Tag vorwärts oder rückwärts zu zählen. Neben diesem Iterator werden auch boost::gregorian::week_iterator, boost::gregorian::month_iterator und boost::gregorian::year_iterator angeboten, die jeweils Wochen, Monate oder Jahre vor- und zurückspringen.

Im obigen Programm wird außerdem die Funktion boost::date_time::next_weekday() verwendet, die das Datum des nächsten Wochentags ausgehend von einem Zeitpunkt zurückgibt. So gibt obiges Programm 2009-Jan-09 aus, weil dies das Datum für den ersten Freitag nach dem 6. Januar 2009 ist.


10.3 Ortsunabhängige Zeitpunkte

Während mit boost::gregorian::date ein Datum erstellt werden kann, kann boost::posix_time::ptime verwendet werden, um einen ortsunabhängigen Zeitpunkt zu definieren. boost::posix_time::ptime greift hierzu auf boost::gregorian::date zu, speichert darüberhinaus aber eine Uhrzeit.

Um boost::posix_time::ptime verwenden zu können, müssen Sie die Headerdatei boost/date_time/posix_time/posix_time.hpp einbinden.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0)); 
  boost::gregorian::date d = pt.date(); 
  std::cout << d << std::endl; 
  boost::posix_time::time_duration td = pt.time_of_day(); 
  std::cout << td << std::endl; 
} 

Sie initialisieren ein Objekt vom Typ boost::posix_time::ptime, indem Sie dem Konstruktor als ersten Parameter ein Datum vom Typ boost::gregorian::date und als zweiten Parameter eine Zeitdauer vom Typ boost::posix_time::time_duration übergeben. Die drei Parameter, die dem Konstruktor von boost::posix_time::time_duration übergeben werden, bestimmen die Uhrzeit. Im obigen Programm wird also ein Zeitpunkt für exakt 12 Uhr Mittags am 5. Januar 2009 definiert.

Über die beiden Methoden date() und time_of_day(), die im obigen Beispiel verwendet werden, können Sie das Datum und die Uhrzeit abfragen.

So wie der Standardkonstruktor von boost::gregorian::date ein ungültiges Datum erstellt, ist auch ein Zeitpunkt vom Typ boost::posix_time::ptime ungültig, wenn der Standardkonstruktor ausgeführt wird. Sie können auch explizit einen ungültigen Zeitpunkt erstellen, indem Sie boost::date_time::not_a_date_time als einzigen Parameter an den Konstruktor von boost::posix_time::ptime übergeben.

So wie Kalenderdaten vom Typ boost::gregorian::date über den Aufruf von Methoden anderer Objekte und freistehender Funktionen erzeugt werden können, bietet Boost.DateTime entsprechende Objekte und freistehende Funktionen zum Erzeugen von Zeitpunkten an.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt = boost::posix_time::second_clock::universal_time(); 
  std::cout << pt.date() << std::endl; 
  std::cout << pt.time_of_day() << std::endl; 

  pt = boost::posix_time::from_iso_string("20090105T120000"); 
  std::cout << pt.date() << std::endl; 
  std::cout << pt.time_of_day() << std::endl; 
} 

Die Klasse boost::posix_time::second_clock repräsentiert eine Uhr, die die aktuelle Zeit zurückgibt. Die Methode universal_time(), die im obigen Programm aufgerufen wird, gibt die UTC-Uhrzeit zurück. Wenn Sie nicht die Weltzeit verwenden möchten, sondern die aktuelle Zeit in der Zeitzone, in der Sie sich befinden, rufen Sie local_time() auf.

Boost.DateTime bietet eine weitere Klasse boost::posix_time::microsec_clock an, die die aktuelle Uhrzeit einschließlich Mikrosekunden zurückgibt. Wenn Sie eine höhere Auflösung benötigen, können Sie diese Klasse verwenden.

Freistehende Funktionen wie boost::posix_time::from_iso_string() können verwendet werden, um einen Zeitpunkt von einem String in ein Objekt vom Typ boost::posix_time::ptime umzuwandeln. Die Funktion boost::posix_time::from_iso_string() setzt dabei voraus, dass der Zeitpunkt im String im ISO 8601-Format gespeichert ist.

Neben boost::posix_time::ptime bietet Boost.DateTime auch eine Klasse boost::posix_time::time_duration an, um eine Zeitdauer zu beschreiben. Sie haben diese Klasse bereits gesehen, da der Konstruktor von boost::posix_time::ptime als zweiten Parameter ein Objekt vom Typ boost::posix_time::time_duration erwartet. Sie können die Klasse boost::posix_time::time_duration aber selbstverständlich auch ohne boost::posix_time::ptime verwenden.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::time_duration td(16, 30, 0); 
  std::cout << td.hours() << std::endl; 
  std::cout << td.minutes() << std::endl; 
  std::cout << td.seconds() << std::endl; 
  std::cout << td.total_seconds() << std::endl; 
} 

Während Sie mit hours(), minutes() und seconds() die Werte erhalten, die als Parameter dem Konstruktor übergeben wurden, können Sie mit Methoden wie total_seconds() auf einfache Weise zusätzliche Informationen erhalten. So gibt Ihnen total_seconds() die Anzahl aller Sekunden zurück.

Sie können dem Konstruktor von boost::posix_time::time_duration beliebige Werte übergeben. Es gibt also zum Beispiel keine Obergrenze von 24 Stunden.

So wie bei Kalenderdaten kann auch mit Zeitpunkten und Zeiträumen gerechnet werden.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0)); 
  boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(18, 30, 0)); 
  boost::posix_time::time_duration td = pt2 - pt1; 
  std::cout << td.hours() << std::endl; 
  std::cout << td.minutes() << std::endl; 
  std::cout << td.seconds() << std::endl; 
} 

Wenn zwei Zeitpunkte vom Typ boost::posix_time::ptime mit einem Minuszeichen verknüpft werden, erhält man ein Objekt vom Typ boost::posix_time::time_duration, das den Zeitraum zwischen den beiden Zeitpunkten angibt.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0)); 
  boost::posix_time::time_duration td(6, 30, 0); 
  boost::posix_time::ptime pt2 = pt1 + td; 
  std::cout << pt2.time_of_day() << std::endl; 
} 

Wie Sie anhand des obigen Beispiels sehen, kann ein Zeitraum mit dem Pluszeichen auch zu einem Zeitpunkt hinzugefügt werden. In diesem Fall wird ein neuer Zeitpunkt erhalten. Obiges Programm gibt 18:30:00 auf die Standardausgabe aus.

Sie haben sicher bereits bemerkt, dass Boost.DateTime für Kalenderdaten und beliebige Zeitpunkte die gleichen Konzepte verwendet. So wie es jeweils eine Klasse gibt, die einen Zeitpunkt und einen Zeitraum beschreibt, gibt es auch eine Klasse, die eine Periode repräsentiert. Für Kalenderdaten heißt diese Klasse boost::gregorian::date_period, für beliebige Zeitpunkte boost::posix_time::time_period. Und so wie der Konstruktor von boost::gregorian::date_period als Parameter zwei Kalenderdaten erwartet, müssen dem Konstruktor von boost::posix_time::time_period zwei Zeitpunkte übergeben werden.

#include <boost/date_time/posix_time/posix_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0)); 
  boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(18, 30, 0)); 
  boost::posix_time::time_period tp(pt1, pt2); 
  std::cout << tp.contains(pt1) << std::endl; 
  std::cout << tp.contains(pt2) << std::endl; 
} 

Die Klasse boost::posix_time::time_period funktioniert grundsätzlich genauso wie boost::gregorian::date_period. So wird unter anderem eine Methode contains() angeboten, die für jeden Zeitpunkt, der sich innerhalb der Periode befindet, true zurückgibt. Da der Zeitpunkt, der dem Konstruktor von boost::posix_time::time_period als zweiter Parameter übergeben wurde, gerade nicht mehr zur Periode zählt, gibt obiges Programm beim zweiten Aufruf von contains() false zurück.

Neben contains() bietet boost::posix_time::time_period weitere Methoden an wie intersection(), um eine Schnittmenge zweier Perioden zu ermitteln, oder merge(), um zwei Perioden, die sich überschneiden, zu einer neuen Periode zu vereinigen.

Abschließend soll der Iterator boost::posix_time::time_iterator vorgestellt werden, mit dem über Zeitpunkte iteriert werden kann.

#include <boost/date_time/local_time/local_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 05), boost::posix_time::time_duration(12, 0, 0)); 
  boost::posix_time::time_iterator it(pt, boost::posix_time::time_duration(6, 30, 0)); 
  std::cout << *++it << std::endl; 
  std::cout << *++it << std::endl; 
} 

Im obigen Programm wird der Iterator it verwendet, um ausgehend vom Zeitpunkt pt jeweils sechseinhalb Stunden zu springen. Da der Iterator zweimal inkrementiert wird, gibt das Programm entsprechend 2009-Jan-05 18:30:00 und 2009-Jan-06 01:00:00 aus.


10.4 Ortsabhängige Zeitpunkte

Der Unterschied zu im vorherigen Abschnitt kennengelernten ortsunabhängigen Zeitpunkten ist, das ortsabhängige Zeitpunkte Zeitzonen berücksichtigen. Dazu bietet Boost.DateTime eine Klasse boost::local_time::local_date_time an, die die Klasse boost::local_time::posix_time_zone verwendet, in der Einstellungen zur Zeitzone gespeichert sind. Um auf diese Klassen zugreifen zu können, muss die Headerdatei boost/date_time/local_time/local_time.hpp eingebunden werden.

#include <boost/date_time/local_time/local_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+1")); 
  boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0)); 
  boost::local_time::local_date_time dt(pt, tz); 
  std::cout << dt.utc_time() << std::endl; 
  std::cout << dt << std::endl; 
  std::cout << dt.local_time() << std::endl; 
  std::cout << dt.zone_name() << std::endl; 
} 

Der Konstruktor der Klasse boost::local_time::local_date_time erwartet als ersten Parameter ein Objekt vom Typ boost::posix_time::ptime und als zweiten Parameter ein Objekt vom Typ boost::local_time::time_zone_ptr. Es handelt sich dabei um eine Typdefinition für boost::shared_ptr<boost::local_time::posix_time_zone>. Wie Sie sehen wird also kein Objekt vom Typ boost::local_time::posix_time_zone übergeben, sondern ein smart pointer auf dieses. So können sich mehrere Objekte vom Typ boost::local_time::local_date_time eine Zeitzone teilen. Wenn das letzte Objekt zerstört wird, wird automatisch das entsprechende Objekt, das die Zeitzone repräsentiert, freigegeben.

Um ein Objekt vom Typ boost::local_time::posix_time_zone zu erstellen, wird dem Konstruktor als einziger Parameter ein String übergeben, der die Zeitzone beschreibt. Im obigen Programm wird angegeben, dass es sich bei der Zeitzone um Mitteleuropa handelt: CET ist die Abkürzung für Central European Time. Da CET eine Stunde vor UTC liegt, wird als Abweichung in Stunden +1 angegeben. Boost.DateTime interpretiert Abkürzungen für Zeitzonen also nicht selbst und weiß nicht, was CET bedeutet. Sie müssen daher immer eine Abweichung in Stunden angeben. Wollen Sie keine Abweichung, geben Sie einfach +0 an.

Wenn Sie obiges Programm ausführen, wird 2009-Jan-05 12:00:00, 2009-Jan-05 13:00:00 CET, 2009-Jan-05 13:00:00 und CET auf die Standardausgabe ausgegeben. Werte, mit denen Objekte vom Typ boost::posix_time::ptime und boost::local_time::local_date_time initialisiert werden, werden also grundsätzlich auf die UTC-Zeitzone bezogen. Erst bei der Ausgabe eines Objekts vom Typ boost::local_time::local_date_time auf die Standardausgabe und beim Aufruf von local_time() wird die Abweichung in Stunden zur Berechnung der lokalen Uhrzeit herangezogen.

#include <boost/date_time/local_time/local_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+1")); 
  boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0)); 
  boost::local_time::local_date_time dt(pt, tz); 
  std::cout << dt.local_time() << std::endl; 
  boost::local_time::time_zone_ptr tz2(new boost::local_time::posix_time_zone("EET+2")); 
  std::cout << dt.local_time_in(tz2).local_time() << std::endl; 
} 

Wenn Sie die Methode local_time() verwenden, wird die Abweichung in der Zeitzone berücksichtigt. Wenn der UTC-Zeitpunkt 12 Uhr Mittags in dt für die Zeitzone CET berechnet werden soll, bedeutet das, dass eine Stunde hinzugefügt werden muss. Denn CET ist der Zeitzone UTC um eine Stunde voraus. Die Methode local_time() gibt demnach 2009-Jan-05 13:00:00 zurück, was auf die Standardausgabe ausgegeben wird.

Die Methode local_time_in() interpretiert den Zeitpunkt in dt so als läge er in der Zeitzone, die als Parameter der Methode local_time_in() übergeben wird. Für obiges Programm bedeutet das, dass 12 Uhr Mittags in UTC 14 Uhr in EET ergibt. EET ist die Abkürzung für Eastern European Time, die um zwei Stunden vor UTC liegt.

Nachdem Sie ortsabhängige Zeitpunkte kennengelernt haben, wird Ihnen abschließend die ortsabhängige Periode vorgestellt. Dazu stellt Boost.DateTime die Klasse boost::local_time::local_time_period zur Verfügung.

#include <boost/date_time/local_time/local_time.hpp> 
#include <iostream> 

int main() 
{ 
  boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+0")); 
  boost::posix_time::ptime pt1(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0)); 
  boost::local_time::local_date_time dt1(pt1, tz); 
  boost::posix_time::ptime pt2(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(18, 0, 0)); 
  boost::local_time::local_date_time dt2(pt2, tz); 
  boost::local_time::local_time_period tp(dt1, dt2); 
  std::cout << tp.contains(dt1) << std::endl; 
  std::cout << tp.contains(dt2) << std::endl; 
} 

Der Konstruktor von boost::local_time::local_time_period erwartet zwei Parameter vom Typ boost::local_time::local_date_time. So wie bei den anderen Datentypen für Perioden, die Boost.DateTime anbietet, ist der zweite Parameter, der den Endzeitpunkt darstellt, gerade nicht mehr in der Periode enthalten. Über die in diesem Kapitel mehrfach erwähnten Methoden contains(), intersection(), merge() und andere können auch bei boost::local_time::local_time_period Perioden verarbeitet werden.


10.5 Formatierte Ein- und Ausgabe

Wenn Sie die Beispiele in diesem Kapitel ausgeführt haben, haben Sie Ergebnisse wie 2009-Jan-07 erhalten. Möglicherweise ziehen Sie es jedoch vor, wenn die Ergebnisse anders formatiert werden. Boost.DateTime bietet mit boost::date_time::date_facet und boost::date_time::time_facet Klassen an, mit denen Sie Kalenderdaten und Zeitpunkte formatieren können.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 
#include <locale> 

int main() 
{ 
  boost::gregorian::date d(2009, 1, 7); 
  boost::gregorian::date_facet *df = new boost::gregorian::date_facet("%A, %d %B %Y"); 
  std::cout.imbue(std::locale(std::cout.getloc(), df)); 
  std::cout << d << std::endl; 
} 

Boost.DateTime greift auf das aus dem C++ Standard bekannte Konzept der Locales zu. Es wurde im Kapitel 5, String-Verarbeitung in aller Kürze vorgestellt, da es zum Verständis der in diesem Kapitel besprochenen Bibliotheken wichtig ist. Um nun mit Boost.DateTime ein Kalenderdatum zu formatieren, müssen Sie ein Objekt vom Typ boost::date_time::date_facet erstellen und es in einem Locale installieren. Dem Konstruktor von boost::date_time::date_facet übergeben Sie dabei einen String, der das neue Format beschreibt. Im obigen Beispiel ist %A, %d %B %Y angegeben, was bedeutet, dass der Wochentag gefolgt vom Datum mit ausgeschriebenem Monatsnamen ausgegeben wird: Wednesday, 07 January 2009.

Boost.DateTime bietet zahlreiche Formatierungsflags an, die jeweils aus einem Prozentzeichen gefolgt von einem Buchstaben bestehen. Die Dokumentation von Boost.DateTime enthält eine vollständige Übersicht über alle unterstützten Formatierungsflags. So ist dort unter anderem angegeben, dass mit %A der Name des Wochentags ausgegeben wird.

Wenn Ihr Programm von Anwendern in Deutschland oder anderen deutschsprachigen Ländern verwendet wird, ziehen Sie es sicherlich vor, wenn sowohl der Wochentag als auch der Monat in Deutsch ausgegeben wird und nicht in Englisch.

#include <boost/date_time/gregorian/gregorian.hpp> 
#include <iostream> 
#include <locale> 
#include <string> 
#include <vector> 

int main() 
{ 
  std::locale::global(std::locale("German")); 
  std::string months[12] = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" }; 
  std::string weekdays[7] = { "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag" }; 
  boost::gregorian::date d(2009, 1, 7); 
  boost::gregorian::date_facet *df = new boost::gregorian::date_facet("%A, %d. %B %Y"); 
  df->long_month_names(std::vector<std::string>(months, months + 12)); 
  df->long_weekday_names(std::vector<std::string>(weekdays, weekdays + 7)); 
  std::cout.imbue(std::locale(std::cout.getloc(), df)); 
  std::cout << d << std::endl; 
} 

Sie können die Namen von Wochentagen und Monaten ändern, indem Sie Vektoren mit den entsprechenden Namen an die Methoden long_month_names() und long_weekday_names() der Klasse boost::date_time::date_facet übergeben. Das obige Programm gibt daraufhin für den 7.1.2009 Mittwoch, 07. Januar 2009 aus.

Boost.DateTime ist sehr flexibel, was die formatierte Datenein- und -ausgabe betrifft. So gibt es neben den beiden Klassen boost::date_time::date_facet und boost::date_time::time_facet zur Datenausgabe die Klassen boost::date_time::date_input_facet und boost::date_time::time_input_facet zur formatierten Dateneingabe. Alle vier Klassen bieten zahlreiche Methoden an, mit denen die Ein- und Ausgabe verschiedener Objekte aus Boost.DateTime konfiguriert werden kann. So ist es zum Beispiel auch möglich anzugeben, wie Perioden vom Typ boost::gregorian::date_period ein- und ausgegeben werden können. Aufgrund der vielfältigen Möglichkeiten zur formatierten Datenein- und -ausgabe ist der Blick in die Dokumentation von Boost.DateTime unerlässlich.


10.6 Aufgaben

Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.

  1. Erstellen Sie ein Programm, das die Wochentage für den nächsten 24. Dezember und die beiden darauf folgenden Feiertage ausgibt.

  2. Berechnen Sie Ihr Alter in Tagen. Ihr Programm soll dabei automatisch das aktuelle Datum ermitteln und zur Berechnung verwenden.